"""Kubernetes metrics."""

import functools
import json
import os
import pathlib

from cki_lib import yaml
from cki_lib.cronjob import CronJob
from cki_lib.logger import get_logger
from kubernetes import client
from kubernetes import config
from kubernetes.utils.quantity import parse_quantity
import prometheus_client

KUBERNETES_CONFIG = yaml.load(contents=os.environ.get('KUBERNETES_CONFIG', '{}'))

LOGGER = get_logger(__name__)


class KubernetesMetrics(CronJob):
    """Calculate Kubernetes metrics."""

    schedule = '* * * * *'

    metric_usage = prometheus_client.Gauge(
        'cki_k8s_resource_usage',
        'The number of used resources by a container',
        ['namespace', 'pod', 'container', 'resource']
    )

    @functools.cached_property
    def api_client(self):
        """Return a k8s api client."""
        config.load_incluster_config()
        return client.ApiClient()

    @functools.cached_property
    def namespace(self):
        """Allow to override the namespace in unit tests."""
        return pathlib.Path(
            '/var/run/secrets/kubernetes.io/serviceaccount/namespace'
        ).read_text(encoding='utf8')

    def update_cluster(self):
        """Update the metrics for a single cluster."""
        current_containers = set()

        for namespace in KUBERNETES_CONFIG.get('namespaces', [self.namespace]):
            response = self.api_client.call_api(
                f'/apis/metrics.k8s.io/v1beta1/namespaces/{namespace}/pods',
                'GET',
                auth_settings=['BearerToken'],
                response_type='json',
                _preload_content=False
            )
            data = json.loads(response[0].data.decode('utf8'))

            for pod in data['items']:
                for container in pod['containers']:
                    labels = (namespace, pod['metadata']['name'], container['name'])

                    self.metric_usage.labels(*labels, 'memory').set(
                        float(parse_quantity(container['usage']['memory']))
                    )

                    self.metric_usage.labels(*labels, 'cpu').set(
                        float(parse_quantity(container['usage']['cpu']))
                    )

            current_containers |= {
                (namespace, pod['metadata']['name'], container['name'])
                for pod in data['items']
                for container in pod['containers']
            }

        self._delete_missing_containers(current_containers)

    def _delete_missing_containers(self, current_containers: set[tuple[str, str, str]]) -> None:
        """Delete metrics for containers that are not running anymore."""
        # pylint: disable=protected-access
        to_delete = {(n, p, c) for (n, p, c, _) in self.metric_usage._metrics} - current_containers

        for container in to_delete:
            LOGGER.debug('Deleting metrics for: %s', container)
            self.metric_usage.remove(*container, 'memory')
            self.metric_usage.remove(*container, 'cpu')

    def run(self, **_):
        """Update the metrics."""
        self.update_cluster()
